package jXMLReader;

import java.io.File;
import java.util.Hashtable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Provides direct access to the XML configuration file.
 * 
 * @author Kenneth
 * @version 1.0.0
 */
public class XMLReader {

	/**
	 * File path of the XML file.
	 * 
	 * @verion 1.0.0
	 */
	private String filePath;

	/**
	 * Hashtable store all the configuration
	 * 
	 * @verion 1.0.0
	 */
	private Hashtable<String, Object> config = new Hashtable<String, Object>();

	/**
	 * Constructor. It will automatically init the hashtable
	 * 
	 * @param filePath
	 * @verion 1.0.0
	 */
	public XMLReader(String filePath) {
		this.filePath = filePath;
		this.init();
	}

	/**
	 * Init the configuration into a hashtable
	 * 
	 */
	private void init() {
		try {
			File file = new File(this.filePath);
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setIgnoringElementContentWhitespace(true);
			dbf.setIgnoringComments(true);
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(file);
			doc.getDocumentElement().normalize();

			this.getKeyAndValue(doc.getDocumentElement());

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Insert the key and value of the configuration into the hashtable
	 * 
	 * @param node
	 *            Root node
	 */
	private void getKeyAndValue(Node node) {

		if (node.hasChildNodes()) {
			for (int i = 0; i < node.getChildNodes().getLength(); i++) {
				this.getKeyAndValue(node.getChildNodes().item(i), node
						.getNodeName());
			}
		}
	}

	/**
	 * Insert the key and value of the configuration into the hashtable
	 * 
	 * @param node
	 * @param prefix
	 */
	private void getKeyAndValue(Node node, String prefix) {

		if (this.hasChildNode(node)) {
			for (int i = 0; i < node.getChildNodes().getLength(); i++) {
				this.getKeyAndValue(node.getChildNodes().item(i), prefix + "."
						+ node.getNodeName());
			}
		} else if (!node.getNodeName().equalsIgnoreCase("#text")
				&& node.getNodeName().trim().length() > 0) {
			insertToHashTable(prefix + "." + node.getNodeName(), node
					.getTextContent());
		}

	}

	/**
	 * Check if the node contains child node or not.
	 * 
	 * @param Checking
	 *            node
	 * @return node return true if having a child node, otherwise return false
	 * @version 1.0.0
	 */
	private boolean hasChildNode(Node node) {
		if (node.hasChildNodes()) {
			NodeList childNodes = node.getChildNodes();
			for (int i = 0; i < childNodes.getLength(); i++) {
				if (!childNodes.item(i).getNodeName().equalsIgnoreCase("#text")) {
					return true;
				}
			}
			return false;
		}
		return false;
	}

	private void insertToHashTable(String key, String value) {

		// Check Integer
		try {
			this.config.put(key, Integer.parseInt(value));
			return;
		} catch (Exception e) {

		}

		// Check Double
		try {
			this.config.put(key, Double.parseDouble(value));
			return;
		} catch (Exception e) {

		}

		// Check Boolean
		if (value.equalsIgnoreCase("true")) {
			this.config.put(key, Boolean.TRUE);
			return;
		} else if (value.equalsIgnoreCase("false")) {
			this.config.put(key, Boolean.FALSE);
			return;
		}

		// String
		this.config.put(key, value);
	}

	/**
	 * Get all the configuration
	 * 
	 * @return a hashtable includes all the configuration setting
	 */
	public Hashtable<String, Object> getConfig() {
		return this.config;
	}

	/**
	 * Get the configuration value by using the key. <br>
	 * 
	 * @param key
	 *            Key of the configuration
	 * @return Value of the configuration (Object)
	 */
	public Object getConfig(String key) {
		return this.config.get(key);
	}
}
